﻿using DotNetCommon.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json;

namespace DBUtil;

public partial class DBAccess
{
    #region reader转model的编译表达式
    private static readonly ConcurrentDictionary<(string readerCode, Type type), object> cacheDictionary = [];
    private static Func<DbDataReader, object> GetFuncSimple() => reader => reader.GetValue(0);
    public Func<DbDataReader, T> GetReaderDeserialization<T>(DbDataReader reader)
    {
        var type = typeof(T);
        var func = GetReaderDeserialization(reader, type);
        return reader => func(reader).To<T>();
    }

    public Func<DbDataReader, object> GetReaderDeserialization(Type type, DbDataReader reader)
    {
        var func = GetReaderDeserialization(reader, type);
        return reader => func(reader);
    }

    private static readonly MethodInfo m_GetFieldType = null;
    private static readonly MethodInfo m_GetValue = null;
    private static readonly MethodInfo m_IsDBNull = null;
    private static readonly MethodInfo m_GetByte = null;
    private static readonly MethodInfo m_GetDateTime = null;
    private static readonly MethodInfo m_GetDecimal = null;
    private static readonly MethodInfo m_GetDouble = null;
    private static readonly MethodInfo m_GetFloat = null;
    private static readonly MethodInfo m_GetGuid = null;
    private static readonly MethodInfo m_GetInt16 = null;
    private static readonly MethodInfo m_GetInt32 = null;
    private static readonly MethodInfo m_GetInt64 = null;
    private static readonly MethodInfo m_GetString = null;
    private static readonly MethodInfo m_GetBoolean = null;
    private static readonly MethodInfo m_GetChar = null;
    private static readonly MethodInfo m_DeserializeObject = null;
    private static readonly MethodInfo m_ToString = null;
    private static readonly MethodInfo m_To = null;

    private static readonly MethodInfo m_ConvertToSByte = null;
    private static readonly MethodInfo m_ConvertToUInt16 = null;
    private static readonly MethodInfo m_ConvertToUInt32 = null;
    private static readonly MethodInfo m_ConvertToUInt64 = null;
    static DBAccess()
    {
        var readerType = typeof(DbDataReader);
        m_GetFieldType = readerType.GetMethod("GetFieldType", [typeof(int)]);
        m_GetValue = readerType.GetMethod("GetValue", [typeof(int)]);
        m_IsDBNull = readerType.GetMethod("IsDBNull", [typeof(int)]);
        m_GetByte = readerType.GetMethod("GetByte", [typeof(int)]);
        m_GetDateTime = readerType.GetMethod("GetDateTime", [typeof(int)]);
        m_GetDecimal = readerType.GetMethod("GetDecimal", [typeof(int)]);
        m_GetDouble = readerType.GetMethod("GetDouble", [typeof(int)]);
        m_GetFloat = readerType.GetMethod("GetFloat", [typeof(int)]);
        m_GetGuid = readerType.GetMethod("GetGuid", [typeof(int)]);
        m_GetInt16 = readerType.GetMethod("GetInt16", [typeof(int)]);
        m_GetInt32 = readerType.GetMethod("GetInt32", [typeof(int)]);
        m_GetInt64 = readerType.GetMethod("GetInt64", [typeof(int)]);
        m_GetString = readerType.GetMethod("GetString", [typeof(int)]);
        m_GetBoolean = readerType.GetMethod("GetBoolean", [typeof(int)]);
        m_GetChar = readerType.GetMethod("GetChar", [typeof(int)]);
        m_DeserializeObject = typeof(System.Text.Json.JsonSerializer).GetMethod("Deserialize", [typeof(string), typeof(Type), typeof(JsonSerializerOptions)]);
        m_ToString = typeof(object).GetMethod("ToString", []);
        m_To = typeof(ObjectExtensions).GetMethod("To", [typeof(object), typeof(Type), typeof(object[])]);

        m_ConvertToSByte = typeof(Convert).GetMethod("ToSByte", [typeof(object)]);
        m_ConvertToUInt16 = typeof(Convert).GetMethod("ToUInt16", [typeof(object)]);
        m_ConvertToUInt32 = typeof(Convert).GetMethod("ToUInt32", [typeof(object)]);
        m_ConvertToUInt64 = typeof(Convert).GetMethod("ToUInt64", [typeof(object)]);
    }

    public Func<DbDataReader, object> GetReaderDeserialization(DbDataReader reader, Type type)
    {
        if (IsSimple(type)) return GetFuncSimple();
        var counter = reader.FieldCount;
        var names = new List<string>(counter);
        for (var i = 0; i < counter; i++) names.Add(reader.GetName(i));
        var readerCode = names.ToStringSeparated("#");
        var func = cacheDictionary.GetOrAdd((readerCode, type), key =>
        {
            //生成表达式
            var para = Expression.Parameter(typeof(DbDataReader), "reader");
            var variable = Expression.Variable(type, "res");
            Expression assign = null;
            var assignProps = new List<BinaryExpression>();
            var reflect = key.type.GetClassGenericFullName();

            var isValueTuple = false;
            if (reflect.Name.StartsWith("System.ValueTuple<") || reflect.Name.StartsWith("System.Tuple<"))
            {
                //db.SelectModel<(string name, int age, DateTime birth)>
                //db.SelectModel<Tuple<string, int, DateTime>>
                //按构造函数的顺序即可 不用名称匹配
                var ctorVals = new List<Expression>();
                var ctor = key.type.GetConstructor(reflect.GenericTypes.Select(i => i.type).ToArray());

                //ValueTuple<T1,T2,T3,T4,T5,T6,T7,TRest> 第8个在套娃
                //Tuple<...>也一样
                var circles = new List<(ConstructorInfo circleCtor, List<Expression> circleCtorVals)> { (ctor, ctorVals) };
                var index = 0;
                while (index < names.Count)
                {
                    Expression exp = null;
                    if (index > 0 && index % 7 == 0)
                    {
                        var circleType = reflect.GenericTypes[7].type;
                        reflect = circleType.GetClassGenericFullName();
                        var circleCtor = circleType.GetConstructor(reflect.GenericTypes.Select(i => i.type).ToArray());
                        var circleCtorVals = new List<Expression>();
                        circles.Add((circleCtor, circleCtorVals));
                    }
                    //超出了 如: db.SelectModel<(int id,int age)>(select id,age,name from test where id = 1);
                    if (index % 7 > reflect.GenericTypes.Count - 1) break;
                    exp = mapValue(para, names[index], reflect.GenericTypes[index % 7].type);
                    circles.LastOrDefault().circleCtorVals.Add(exp);
                    index++;
                }
                for (var i = circles.Count - 1; i >= 0; i--)
                {
                    var exp = Expression.New(circles[i].circleCtor, circles[i].circleCtorVals);
                    if (i == 0) assign = Expression.Assign(variable, exp);
                    else circles[i - 1].circleCtorVals.Add(exp);
                }
                if (reflect.Name.StartsWith("System.ValueTuple<")) isValueTuple = true;
            }
            else
            {
                var exp = fetchInstance(para, [], names, type);
                assign = Expression.Assign(variable, exp);
            }

            LabelTarget labelTarget = null;
            LabelExpression labelExpression = null;
            GotoExpression gotoExpression = null;
            if (isValueTuple)
            {
                labelTarget = Expression.Label(typeof(object));
                var convertExpression = Expression.Convert(variable, typeof(object));
                labelExpression = Expression.Label(labelTarget, convertExpression);
                gotoExpression = Expression.Return(labelTarget, convertExpression, typeof(object));
            }
            else
            {
                labelTarget = Expression.Label(type);
                labelExpression = Expression.Label(labelTarget, variable);
                gotoExpression = Expression.Return(labelTarget, variable, type);
            }

            var expressions = new List<Expression> { assign };
            expressions.AddRange(assignProps);
            expressions.Add(gotoExpression);
            expressions.Add(labelExpression);

            //组装表达式块,编译生成委托
            BlockExpression block = null;
            if (isValueTuple) block = Expression.Block(typeof(object), [variable], expressions);
            else block = Expression.Block(type, [variable], expressions);
            var mapperFunc = Expression.Lambda<Func<DbDataReader, object>>(block, para).Compile();
            return mapperFunc;
        }) as Func<DbDataReader, object>;
        return func;

        Expression fetchInstance(ParameterExpression para, List<string> nests, List<string> names, Type type)
        {
            if (type.IsAnonymous())
            {
                //匿名类: .ToList(i=>new { Id=i.Id,Name=i.Name }
                var ctorVals = new List<Expression>();
                var ctor = type.GetConstructors().FirstOrDefault();
                var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                var prefix = nests.ToStringSeparated(".");
                for (var i = 0; i < props.Length; i++)
                {
                    Expression exp = null;
                    if (DBAccess.IsSimple(props[i].PropertyType))
                    {
                        var expectName = prefix.IsNullOrWhiteSpace() ? props[i].Name : $"{prefix}.{props[i].Name}";
                        exp = mapValue(para, expectName, props[i].PropertyType);
                    }
                    else
                    {
                        //嵌套类
                        nests.Add(props[i].Name);
                        exp = fetchInstance(para, nests, names, props[i].PropertyType);
                        nests.RemoveAt(nests.Count - 1);
                    }
                    ctorVals.Add(exp);
                }
                return Expression.New(ctor, ctorVals);
            }
            else
            {
                //属性名-列名匹配:
                // SelectModel<TUser>("select id,name from tuser")
                // ToList(i=>new TUser{ Id = i.Id,Name=i.Name})
                var entityInfo = GetEntityInfoInternal(type, false);

                //使用属性赋值
                var ctor = type.GetConstructor([]) ?? throw new Exception($"类型 [{type.GetClassFullName()}] 必须有无参构造函数!");
                var binds = new List<MemberBinding>();
                var newExp = Expression.New(ctor);
                var prefix = nests.IsNullOrEmpty() ? "" : nests.ToStringSeparated(".") + ".";
                for (var i = 0; i < entityInfo.Props.Count; i++)
                {
                    var expects = new List<string>();
                    var prop = entityInfo.Props[i];
                    var readerColName = string.Empty;
                    if (names.Contains($"{prefix}{prop.ColumnNamePure}"))
                    {
                        readerColName = $"{prefix}{prop.ColumnNamePure}";
                    }
                    else if (names.Contains($"{prefix}{prop.PropNamePure}"))
                    {
                        readerColName = $"{prefix}{prop.PropNamePure}";
                    }
                    else
                    {
                        var idx = -1;
                        if (prop.ColumnNamePure.IsNotNullOrWhiteSpace())
                        {
                            idx = names.FindIndex(i => string.Equals(i, $"{prefix}{prop.ColumnNamePure}", StringComparison.OrdinalIgnoreCase));
                        }
                        if (idx < 0) idx = names.FindIndex(i => string.Equals(i, $"{prefix}{prop.PropNamePure}", StringComparison.OrdinalIgnoreCase));
                        if (idx < 0 && prop.ColumnNamePure.IsNotNullOrWhiteSpace())
                        {
                            idx = names.FindIndex(i => string.Equals(i, $"{prefix}{prop.ColumnNamePure.Replace('_', '\0')}", StringComparison.OrdinalIgnoreCase));
                        }
                        if (idx < 0) idx = names.FindIndex(i => string.Equals(i, $"{prefix}{prop.PropNamePure.Replace('_', '\0')}", StringComparison.OrdinalIgnoreCase));
                        if (idx < 0) continue;
                        readerColName = names[idx];
                    }
                    var exp = mapValue(para, readerColName, prop.Type);
                    binds.Add(Expression.Bind(prop.PropertyInfo, exp));
                }
                //嵌套属性
                if (names.Any(i => i.Contains('.')))
                {
                    var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(i => !DBAccess.IsSimple(i.PropertyType)).ToList();
                    foreach (var prop in props)
                    {
                        var newNests = nests.ToList().AddFluent(prop.Name);
                        var _pre = newNests.ToStringSeparated(".") + '.';
                        var newNames = names.Where(i => i.StartsWith(_pre)).ToList();
                        if (newNames.IsNotNullOrEmpty())
                        {
                            var exp = fetchInstance(para, newNests, names, prop.PropertyType);
                            if (exp != null) binds.Add(Expression.Bind(prop, exp));
                        }
                        newNests.RemoveAt(newNests.Count - 1);
                    }
                }
                return Expression.MemberInit(Expression.New(ctor), binds);
            }
        }

        Expression mapValue(ParameterExpression para, string colName, Type memberType)
        {
            var isNullAble = memberType.IsNullable();
            bool isEnum = isNullAble ? Nullable.GetUnderlyingType(memberType).IsEnum : memberType.IsEnum;
            var typeClassFullName = memberType.GetClassFullName();

            var index = names.IndexOf(colName);
            switch (typeClassFullName)
            {
                #region 非 NullAble<T>
                case "bool":
                    {
                        return Expression.Call(para, m_GetBoolean, Expression.Constant(index));
                    }
                case "char":
                    {
                        return Expression.Call(para, m_GetChar, Expression.Constant(index));
                    }
                case "byte":
                    {
                        return Expression.Call(para, m_GetByte, Expression.Constant(index));
                    }
                case "sbyte":
                    {
                        return Expression.Call(m_ConvertToSByte, Expression.Call(para, m_GetValue, Expression.Constant(index)));
                    }
                case "System.DateTime":
                    {
                        return Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(DateTime), typeof(Type))),
                            Expression.Call(para, m_GetDateTime, Expression.Constant(index)),
                            Expression.Convert(Expression.Call(null, m_To, Expression.Call(para, m_GetValue, Expression.Constant(index)), Expression.Constant(memberType), Expression.Constant(null, typeof(object[]))), memberType));
                    }
                case "System.DateTimeOffset":
                    {
                        return Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(DateTime), typeof(Type))),
                            Expression.New(
                                typeof(DateTimeOffset).GetConstructor([typeof(DateTime)]),
                                Expression.Call(para, m_GetDateTime, Expression.Constant(index))),
                            Expression.Convert(Expression.Call(null, m_To, Expression.Call(para, m_GetValue, Expression.Constant(index)), Expression.Constant(memberType), Expression.Constant(null, typeof(object[]))), memberType));
                    }
                case "decimal":
                    {
                        return Expression.Call(para, m_GetDecimal, Expression.Constant(index));
                    }
                case "double":
                    {
                        return Expression.Call(para, m_GetDouble, Expression.Constant(index));
                    }
                case "float":
                    {
                        return Expression.Call(para, m_GetFloat, Expression.Constant(index));
                    }
                case "System.Guid":
                    {
                        return Expression.Call(para, m_GetGuid, Expression.Constant(index));
                    }
                case "short":
                    {
                        return Expression.Call(para, m_GetInt16, Expression.Constant(index));
                    }
                case "ushort":
                    {
                        return Expression.Call(m_ConvertToUInt16, Expression.Call(para, m_GetValue, Expression.Constant(index)));
                    }
                case "int":
                    {
                        return Expression.Call(para, m_GetInt32, Expression.Constant(index));
                    }
                case "uint":
                    {
                        return Expression.Call(m_ConvertToUInt32, Expression.Call(para, m_GetValue, Expression.Constant(index)));
                    }
                case "long":
                    {
                        return Expression.Call(para, m_GetInt64, Expression.Constant(index));
                    }
                case "ulong":
                    {
                        return Expression.Call(m_ConvertToUInt64, Expression.Call(para, m_GetValue, Expression.Constant(index)));
                    }
                case "string":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetString, Expression.Constant(index)), memberType));
                    }
                case "byte[]":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetValue, Expression.Constant(index)), memberType));
                    }
                #endregion
                #region NullAble<T>
                case "bool?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetBoolean, Expression.Constant(index)), memberType));
                    }
                case "char?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetChar, Expression.Constant(index)), memberType));
                    }
                case "byte?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetByte, Expression.Constant(index)), memberType));
                    }
                case "sbyte?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(m_ConvertToSByte, Expression.Call(para, m_GetValue, Expression.Constant(index))), memberType));
                    }
                case "System.DateTime?":
                    {
                        return
                            Expression.Condition(Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(DateTime), typeof(Type))),
                                    Expression.Convert(Expression.Call(para, m_GetDateTime, Expression.Constant(index)), memberType),
                                    Expression.Convert(Expression.Call(null, m_To, Expression.Call(para, m_GetValue, Expression.Constant(index)), Expression.Constant(memberType), Expression.Constant(null, typeof(object[]))), memberType)));
                    }
                case "System.DateTimeOffset?":
                    {
                        return
                            Expression.Condition(Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(DateTime), typeof(Type))),
                                    Expression.Convert(Expression.New(
                                        typeof(DateTimeOffset).GetConstructor([typeof(DateTime)]),
                                        Expression.Call(para, m_GetDateTime, Expression.Constant(index))), memberType),
                                    Expression.Convert(Expression.Call(null, m_To, Expression.Call(para, m_GetValue, Expression.Constant(index)), Expression.Constant(memberType), Expression.Constant(null, typeof(object[]))), memberType)));
                    }
                case "decimal?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetDecimal, Expression.Constant(index)), memberType));
                    }
                case "double?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType), Expression.Convert(Expression.Call(para, m_GetDouble, Expression.Constant(index)), memberType));
                    }
                case "float?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetFloat, Expression.Constant(index)), memberType));
                    }
                case "System.Guid?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Convert(Expression.Call(para, m_GetGuid, Expression.Constant(index)), memberType), memberType));
                    }
                case "short?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Convert(Expression.Call(para, m_GetInt16, Expression.Constant(index)), memberType), memberType));
                    }
                case "ushort?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(m_ConvertToUInt16, Expression.Call(para, m_GetValue, Expression.Constant(index))), memberType));
                    }
                case "int?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetInt32, Expression.Constant(index)), memberType));
                    }
                case "uint?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(m_ConvertToUInt32, Expression.Call(para, m_GetValue, Expression.Constant(index))), memberType));
                    }
                case "long?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetInt64, Expression.Constant(index)), memberType));
                    }
                case "ulong?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(m_ConvertToUInt64, Expression.Call(para, m_GetValue, Expression.Constant(index))), memberType));
                    }
                case "string?":
                    {
                        return Expression.Condition(
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(null, memberType),
                                Expression.Convert(Expression.Call(para, m_GetString, Expression.Constant(index)), memberType));
                    }
                #endregion
                default: break;
            }
            #region 枚举
            if (isEnum)
            {
                //先判断 reader 中是否能读取为 int, 能的话直接转, 不能的话读取为 string 后, 执行 Enum.Parse()
                //mysql 中列类型(enum,set) 默认读取是string 读取 int 会报错
                if (isNullAble)
                {
                    //reader.isDBNull(idx)?null:
                    //  reader.GetFieldType(idx)==typeof(int)? (EnumTest)reader.GetInt(idx):
                    //    reader.GetFieldType(idx)==typeof(long)?(EnumTest)reader.GetInt64(idx):
                    //      reader.GetFieldType(idx)==typeof(short)?(EnumTest)reader.GetInt16(idx):
                    //        (reader.GetString(idx)==""||reader.GetString(idx)=="0")?(EnumTest)0:
                    //                (EnumTest)Enum.Parse(typeof(EnumTest,reader.GetString(idx),true)
                    //            )
                    return Expression.Condition(Expression.Call(para, m_IsDBNull, Expression.Constant(index)), Expression.Constant(null, memberType),
                                     Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(int), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt32, Expression.Constant(index)), memberType),
                                            Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(long), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt64, Expression.Constant(index)), memberType),
                                                    Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(short), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt16, Expression.Constant(index)), memberType),
                                                            Expression.Condition(
                                                                Expression.Or(Expression.Equal(Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant("", typeof(string))), Expression.Equal(Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant("0", typeof(string)))),
                                                                Expression.Convert(Expression.Constant(0, typeof(int)), memberType), Expression.Convert(Expression.Call(
                                                                typeof(Enum).GetMethod("Parse", [typeof(Type), typeof(string), typeof(bool)]), Expression.Constant(Nullable.GetUnderlyingType(memberType)), Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant(true)), memberType)
                                                            )
                                                    )
                                            )
                                     )
                           );
                }
                else
                {
                    //reader.GetFieldType(idx)==typeof(int)? (EnumTest)reader.GetInt32(idx):
                    //  reader.GetFieldType(idx)==typeof(long)?(EnumTest)reader.GetInt64(idx):
                    //    reader.GetFieldType(idx)==typeof(short)?(EnumTest)reader.GetInt16(idx):
                    //      (reader.isDBNull()?null:
                    //          (reader.GetString(idx)==""||reader.GetString(idx)=="0")?(EnumTest)0:
                    //              (EnumTest)Enum.Parse(typeof(EnumTest,reader.GetString(idx),true)
                    //          )
                    //       )
                    return Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(int), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt32, Expression.Constant(index)), memberType),
                                    Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(long), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt64, Expression.Constant(index)), memberType),
                                            Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(short), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt16, Expression.Constant(index)), memberType),
                                                    Expression.Condition(
                                                        Expression.Or(Expression.Equal(Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant("", typeof(string))), Expression.Equal(Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant("0", typeof(string)))),
                                                        Expression.Convert(Expression.Constant(0, typeof(int)), memberType), Expression.Convert(Expression.Call(
                                                        typeof(Enum).GetMethod("Parse", [typeof(Type), typeof(string), typeof(bool)]), Expression.Constant(memberType), Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant(true)), memberType)
                                                     )
                                            )
                                    )
                           );
                }
            }
            #endregion
            //reader.GetValue(index)
            var dataExp = Expression.Call(para, m_GetValue, Expression.Constant(index));

            if (memberType.IsValueType)
                return Expression.Convert(Expression.Call(null, m_To, dataExp, Expression.Constant(memberType), Expression.Constant(null, typeof(object[]))), memberType);

            // result = isnull?null:(propType)DeserializeObject(getvalue(index).ToString(),propType)
            return Expression.Condition(
                                //reader.IsDBNull(index)
                                Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                Expression.Constant(memberType.GetDefault(), memberType),
                                //(PropType)Newtonsoft.Json.JsonConvert.DeserializeObject(reader.GetValue(index).ToString(),propType)
                                Expression.Convert(
                                    Expression.Call(null, m_DeserializeObject, new Expression[] {
                                                    Expression.Call(dataExp, m_ToString, new Expression[0]), Expression.Constant(memberType),Expression.Constant(null,typeof(JsonSerializerOptions)) }), memberType, null));
        }
    }
    #endregion    
}
